package com.example.seckill.service.impl;

import com.example.seckill.dto.Exposer;
import com.example.seckill.dto.SeckillExecution;
import com.example.seckill.entity.Seckill;
import com.example.seckill.entity.SuccessKilled;
import com.example.seckill.enums.SeckillStateEnum;
import com.example.seckill.exception.RepeatKillException;
import com.example.seckill.exception.SeckillCloseException;
import com.example.seckill.exception.SeckillException;
import com.example.seckill.keys.SuccessKilledKey;
import com.example.seckill.repository.SeckillRepository;
import com.example.seckill.repository.SuccessKilledRepository;
import com.example.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.transaction.Transactional;
import javax.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Service
public class SeckillServiceImpl implements SeckillService {

    private Logger logger = LoggerFactory.getLogger(SeckillServiceImpl.class);

    /**
     * 用于混淆md5
     */
    private final String slat = "as;dlfjasldjf5678asdf%**()__-&^%45";

    @Autowired
    private SeckillRepository seckillRepository;

    @Autowired
    private SuccessKilledRepository successKilledRepository;

    @Override
    public List<Seckill> querySeckillList() {

        return seckillRepository.findAll();
    }

    @Override
    public Seckill getById(long id) {

        Optional<Seckill> optional = seckillRepository.findById(id);
        return optional.orElse(null);
    }

    @Override
    public Exposer exportSeckillUrl(long seckillId) {
        Optional<Seckill> optionalSeckill = seckillRepository.findById(seckillId);
        if (!optionalSeckill.isPresent()) {
            return new Exposer(false, seckillId);
        }
        Seckill seckill = optionalSeckill.get();
        Date startTime = seckill.getStartTime();
        Date endTime = seckill.getEndTime();
        Date now = new Date();

        if (now.before(startTime) || now.after(endTime)) {
            return new Exposer(false, seckillId, now.getTime(), startTime.getTime(), endTime.getTime());
        }
        String md5 = getMd5(seckillId);
        return new Exposer(true, md5, seckillId);
    }

    @Transactional
    @Override
    public SeckillExecution executeSeckill(long seckillId, String userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException {

        try {

            if (StringUtils.isEmpty(md5) || !md5.equals(getMd5(seckillId))) {
                throw new SeckillException("seckill data rewrite");
            }
            //执行秒杀业务逻辑
            Date now = new Date();
            int updateCount = seckillRepository.reduceStock(seckillId, now);
            if (updateCount <= 0) {
                throw new SeckillCloseException("seckill is closed");
            }
            //记录购买行为
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserPhone(userPhone);
            successKilled.setCreateTime(now);
            successKilled.setState(0);
            try {
                SuccessKilled saved = successKilledRepository.save(successKilled);
            } catch (DataIntegrityViolationException e) {
                throw new RepeatKillException("seckill is repeated");
            }
            return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled);

        } catch (RepeatKillException e) {
            throw e;
        } catch (SeckillCloseException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new SeckillException("seckill inner error:" + e.getMessage());
        }
    }


    private String getMd5(long seckillId) {
        String base = seckillId + "/" + slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }
}
